﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using FlickrNet;
using Schedulr.Configuration;
using Schedulr.Helpers;

namespace Schedulr.Controls
{
    /// <summary>
    /// Allows the user to edit the details of one or more pictures.
    /// </summary>
    public partial class PictureEditor : UserControl, INotifyPropertyChanged
    {
        #region Fields

        private IList<Picture> selectedPictures = new List<Picture>();

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="PicturesEditor"/> class.
        /// </summary>
        public PictureEditor()
        {
            InitializeComponent();
            FlickrServer.CurrentUserChanged += new EventHandler<EventArgs>(FlickrServer_CurrentUserChanged);
        }

        #endregion

        #region Event Handlers

        private void FlickrServer_CurrentUserChanged(object sender, EventArgs e)
        {
            this.setsCheckedListBox.Items.Clear();
            this.groupsCheckedListBox.Items.Clear();
            if (FlickrServer.CurrentUserInfo != null)
            {
                foreach (Photoset set in FlickrServer.CurrentUserInfo.Sets)
                {
                    this.setsCheckedListBox.Items.Add(new CheckedListBoxItem(set.Title, set.PhotosetId));
                }

                foreach (MemberGroupInfo group in FlickrServer.CurrentUserInfo.Groups)
                {
                    this.groupsCheckedListBox.Items.Add(new CheckedListBoxItem(group.GroupName, group.GroupId));
                }
            }
            UpdateBindings();
        }

        #endregion

        #region SetPictures

        public void SetPictures(IList<Picture> pictures)
        {
            this.selectedPictures = pictures;
            UpdateBindings();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private void UpdateBindings()
        {
            this.setsCheckedListBox.ItemCheck -= this.setsCheckedListBox_ItemCheck;
            this.groupsCheckedListBox.ItemCheck -= this.groupsCheckedListBox_ItemCheck;

            CheckState uploadWithNext = CheckState.Unchecked;
            string title = string.Empty;
            string description = string.Empty;
            string tags = string.Empty;
            CheckState isPublic = CheckState.Unchecked;
            CheckState isFamily = CheckState.Unchecked;
            CheckState isFriend = CheckState.Unchecked;
            Dictionary<string, int> setCounts = new Dictionary<string, int>();
            Dictionary<string, int> groupCounts = new Dictionary<string, int>();

            if (this.selectedPictures.Count > 0)
            {
                Picture firstPicture = this.selectedPictures[0];
                uploadWithNext = (firstPicture.UploadWithNext ? CheckState.Checked : CheckState.Unchecked);
                title = firstPicture.Title;
                description = firstPicture.Description;
                tags = firstPicture.Tags;
                isPublic = (firstPicture.IsPublic ? CheckState.Checked : CheckState.Unchecked);
                isFamily = (firstPicture.IsFamily ? CheckState.Checked : CheckState.Unchecked);
                isFriend = (firstPicture.IsFriend ? CheckState.Checked : CheckState.Unchecked);

                foreach (Picture picture in this.selectedPictures)
                {
                    foreach (string setId in picture.SetIds)
                    {
                        if (setCounts.ContainsKey(setId))
                        {
                            setCounts[setId] = setCounts[setId] + 1;
                        }
                        else
                        {
                            setCounts.Add(setId, 1);
                        }
                    }
                    foreach (string groupId in picture.GroupIds)
                    {
                        if (groupCounts.ContainsKey(groupId))
                        {
                            groupCounts[groupId] = groupCounts[groupId] + 1;
                        }
                        else
                        {
                            groupCounts.Add(groupId, 1);
                        }
                    }
                }

                if (this.selectedPictures.Count > 1)
                {
                    foreach (Picture picture in this.selectedPictures)
                    {
                        // For string values, see if all pictures have the same value, if not use an empty string.
                        if (!string.Equals(picture.Title, title, StringComparison.CurrentCulture))
                        {
                            title = string.Empty;
                        }
                        if (!string.Equals(picture.Description, description, StringComparison.CurrentCulture))
                        {
                            description = string.Empty;
                        }

                        // Always leave the tags empty since they will be added (not just edited) when dealing with multiple pictures.
                        tags = string.Empty;

                        // For boolean values, see if all pictures have the same value, if not use an indeterminate state.
                        if (uploadWithNext != CheckState.Indeterminate && (picture.UploadWithNext != (uploadWithNext == CheckState.Checked)))
                        {
                            uploadWithNext = CheckState.Indeterminate;
                        }
                        if (isPublic != CheckState.Indeterminate && (picture.IsPublic != (isPublic == CheckState.Checked)))
                        {
                            isPublic = CheckState.Indeterminate;
                        }
                        if (isFamily != CheckState.Indeterminate && (picture.IsFamily != (isFamily == CheckState.Checked)))
                        {
                            isFamily = CheckState.Indeterminate;
                        }
                        if (isFriend != CheckState.Indeterminate && (picture.IsFriend != (isFriend == CheckState.Checked)))
                        {
                            isFriend = CheckState.Indeterminate;
                        }
                    }
                }
            }

            this.uploadWithNextCheckBox.CheckState = uploadWithNext;
            this.titleTextBox.Text = title;
            this.descriptionTextBox.Text = description;
            this.tagsTextBox.Text = tags;
            this.visibilityPublicCheckBox.CheckState = isPublic;
            this.visibilityFamilyCheckBox.CheckState = isFamily;
            this.visibilityFriendsCheckBox.CheckState = isFriend;

            if (FlickrServer.CurrentUserInfo == null)
            {
                this.setsCheckedListBox.Items.Clear();
                foreach (var setId in setCounts.Keys)
                {
                    this.setsCheckedListBox.Items.Add(new CheckedListBoxItem(setId, setId));
                }
                this.groupsCheckedListBox.Items.Clear();
                foreach (var groupId in groupCounts.Keys)
                {
                    this.groupsCheckedListBox.Items.Add(new CheckedListBoxItem(groupId, groupId));
                }
            }

            for (int i = 0; i < this.setsCheckedListBox.Items.Count; i++)
            {
                CheckedListBoxItem item = (CheckedListBoxItem)this.setsCheckedListBox.Items[i];
                CheckState state = CheckState.Unchecked;
                if (setCounts.ContainsKey(item.Id))
                {
                    state = (setCounts[item.Id] == this.selectedPictures.Count ? CheckState.Checked : CheckState.Indeterminate);
                }
                this.setsCheckedListBox.SetItemCheckState(i, state);
            }

            for (int i = 0; i < this.groupsCheckedListBox.Items.Count; i++)
            {
                CheckedListBoxItem item = (CheckedListBoxItem)this.groupsCheckedListBox.Items[i];
                CheckState state = CheckState.Unchecked;
                if (groupCounts.ContainsKey(item.Id))
                {
                    state = (groupCounts[item.Id] == this.selectedPictures.Count ? CheckState.Checked : CheckState.Indeterminate);
                }
                this.groupsCheckedListBox.SetItemCheckState(i, state);
            }

            this.setsCheckedListBox.ItemCheck += this.setsCheckedListBox_ItemCheck;
            this.groupsCheckedListBox.ItemCheck += this.groupsCheckedListBox_ItemCheck;
        }

        #endregion

        #region Data Binding

        private void titleTextBox_Validated(object sender, EventArgs e)
        {
            if (this.selectedPictures.Count > 1 && string.IsNullOrEmpty(this.titleTextBox.Text))
            {
                // Do not set empty strings on multiple pictures; the user may just have selected the field and left.
                return;
            }
            foreach (Picture picture in this.selectedPictures)
            {
                picture.Title = this.titleTextBox.Text.Trim();
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameTitle));
        }

        private void uploadWithNextCheckBox_Validated(object sender, EventArgs e)
        {
            foreach (Picture picture in this.selectedPictures)
            {
                picture.UploadWithNext = this.uploadWithNextCheckBox.Checked;
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameUploadWithNext));
        }

        private void descriptionTextBox_Validated(object sender, EventArgs e)
        {
            if (this.selectedPictures.Count > 1 && string.IsNullOrEmpty(this.descriptionTextBox.Text))
            {
                // Do not set empty strings on multiple pictures; the user may just have selected the field and left.
                return;
            }
            foreach (Picture picture in this.selectedPictures)
            {
                picture.Description = this.descriptionTextBox.Text.Trim();
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameDescription));
        }

        private void tagsTextBox_Validated(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(this.tagsTextBox.Text))
            {
                if (this.selectedPictures.Count == 1)
                {
                    // There is only one picture, edit the tags entirely.
                    this.selectedPictures[0].Tags = this.tagsTextBox.Text.Trim();
                }
                else
                {
                    // Add the tags to all pictures and then clear the text box.
                    foreach (Picture picture in this.selectedPictures)
                    {
                        picture.Tags = picture.Tags.Trim() + " " + this.tagsTextBox.Text.Trim();
                    }
                    this.tagsTextBox.Text = string.Empty;
                }
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameTags));
        }

        private void visibilityPublicCheckBox_Validated(object sender, EventArgs e)
        {
            foreach (Picture picture in this.selectedPictures)
            {
                picture.IsPublic = this.visibilityPublicCheckBox.Checked;
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameIsPublic));
        }

        private void visibilityFamilyCheckBox_Validated(object sender, EventArgs e)
        {
            foreach (Picture picture in this.selectedPictures)
            {
                picture.IsFamily = this.visibilityFamilyCheckBox.Checked;
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameIsFamily));
        }

        private void visibilityFriendsCheckBox_Validated(object sender, EventArgs e)
        {
            foreach (Picture picture in this.selectedPictures)
            {
                picture.IsFriend = this.visibilityFriendsCheckBox.Checked;
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameIsFriend));
        }

        private void setsCheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            CheckedListBoxItem item = (CheckedListBoxItem)this.setsCheckedListBox.Items[e.Index];
            string setId = item.Id;
            foreach (Picture picture in this.selectedPictures)
            {
                List<string> setIds = new List<string>(picture.SetIds);
                if (e.NewValue == CheckState.Unchecked)
                {
                    setIds.Remove(setId);
                }
                else if (e.NewValue == CheckState.Checked && !setIds.Contains(setId))
                {
                    setIds.Add(setId);
                }
                picture.SetIds = setIds.ToArray();
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameSetIds));
        }

        private void groupsCheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            CheckedListBoxItem item = (CheckedListBoxItem)this.groupsCheckedListBox.Items[e.Index];
            string groupId = item.Id;
            foreach (Picture picture in this.selectedPictures)
            {
                List<string> groupIds = new List<string>(picture.GroupIds);
                if (e.NewValue == CheckState.Unchecked)
                {
                    groupIds.Remove(groupId);
                }
                else if (e.NewValue == CheckState.Checked && !groupIds.Contains(groupId))
                {
                    groupIds.Add(groupId);
                }
                picture.GroupIds = groupIds.ToArray();
            }
            OnPropertyChanged(new PropertyChangedEventArgs(Picture.PropertyNameGroupIds));
        }

        #endregion

        #region CheckedListBoxItem Class

        /// <summary>
        /// Represents an item in the checked list box.
        /// </summary>
        private class CheckedListBoxItem
        {
            /// <summary>
            /// The text.
            /// </summary>
            public string Text;

            /// <summary>
            /// The id.
            /// </summary>
            public string Id;

            /// <summary>
            /// Initializes a new instance of the <see cref="CheckedListBoxItem"/> class.
            /// </summary>
            /// <param name="text">The text.</param>
            /// <param name="id">The id.</param>
            public CheckedListBoxItem(string text, string id)
            {
                this.Text = text;
                this.Id = id;
            }

            /// <summary>
            /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
            /// </summary>
            /// <returns>
            /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
            /// </returns>
            public override string ToString()
            {
                return this.Text;
            }
        }

        #endregion

        #region INotifyPropertyChanged Implementation

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the <see cref="E:PropertyChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, e);
            }
        }

        #endregion
    }
}